#include "SimulateVideoEncoder.h"
#include <pthread.h>
#include "Log.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/system_properties.h>
#include <stdio.h>

#define BUFFER_SIZE 921600
void (*pOnVideoFrameChanged)(void* data,int length );
pthread_t 		m_VideoEncoderTid;
pthread_t *		m_VideoEncoderThreadPtr;

volatile int keepEncoderRunning = 1;


// Function to convert NALU data to hex string and print
void print_nalu_as_hex(unsigned char *nalu_data, int nalu_size) {
    char *hex_string = (char *)malloc(nalu_size * 3 + 1); // Each byte as "XX " + null terminator
    char *ptr = hex_string;

    for (int i = 0; i < nalu_size; i++) {
        ptr += sprintf(ptr, "%02X ", nalu_data[i]);
    }

    LOGD("%s\n", hex_string);
    free(hex_string);
}

// Function to find the start code in the H264 bitstream
int find_start_code(unsigned char *buffer, int offset, int length) {
    for (int i = offset; i < length - 3; i++) {
        if (buffer[i] == 0x00 && buffer[i+1] == 0x00 && buffer[i+2] == 0x00 && buffer[i+3] == 0x01) {
            return i;
        }
    }
    return -1;
}

uint8_t determine_nalu_type(unsigned char *buf, int len ) {
	//00 00 00 01 05
	uint8_t ret = 0;
	if( len >=5 )
	{
	    if (buf[0] == 0x00
	    		&&buf[1] == 0x00
	    		&&buf[2] == 0x00
	    		&&buf[3] == 0x01) {
	    	ret = buf[4]&0x1F;
	    }
	}
	return ret;
}

unsigned char h264head_data [ 32 ] =  { 0 };

// Function to send NALU unit to network (simulated here)
void send_nalu(unsigned char *nalu_data, int nalu_size) {
    // Simulate sending NALU to network
    LOGD("Sending NALU of size: %d\n", nalu_size);
    usleep( 73 * 1000 );
    // Actual network sending code would go here
    if( pOnVideoFrameChanged != NULL )
    {
    	uint8_t nalu_type = determine_nalu_type(nalu_data, nalu_size);
//    	print_nalu_as_hex(nalu_data, nalu_size);
    	switch (nalu_type) {
    	        case 7:
    	        	LOGD("NALU Type: SPS\n");
    	        	memset(h264head_data, 0, 32);
    	        	memcpy(h264head_data, nalu_data, nalu_size);
    	            break;
    	        case 8:
    	        	LOGD("NALU Type: PPS\n");
    	        	memcpy(h264head_data + ( 32 - 8), nalu_data, nalu_size);
    	        	pOnVideoFrameChanged( h264head_data, 32);
    	            break;
    	        case 5:
    	        	LOGD("NALU Type: IDR (I Frame)\n");
    	        	pOnVideoFrameChanged( nalu_data, nalu_size);
    	            break;
    	        default:
    	        	LOGD("NALU Type: Other (%d)\n", nalu_type);
    	        	pOnVideoFrameChanged( nalu_data, nalu_size);
    	            break;
    	    }

    }
}

void *videoEncoderThread(void *arg) {
	keepEncoderRunning = 1;
	char udisk[92] = { 0 };
	char h264_path [ 200 ] = { 0 };
	__system_property_get("sys.mount.stableName", udisk);
	//  /mnt/media_rw/83F2-60BC/
	LOGD("videoEncoderThread Prepare open udisk file %s \n",udisk);

	sprintf( h264_path, "/mnt/media_rw/%s%s",udisk, "/simulate.h264" );
	LOGD("videoEncoderThread Prepare open input file %s \n",h264_path);
	while ( keepEncoderRunning ){
//	    FILE *file = fopen("data/800x480.h264", "rb");
	    FILE *file = fopen(h264_path, "rb");
	    if (!file) {
	        LOGD("videoEncoderThread Failed to open input file");
	        break;
	    }
	    LOGD("videoEncoderThread Successed to open input file");
	    unsigned char buffer[BUFFER_SIZE];
	    int bufferOffset = 0;
	    int startCodePos, nextStartCodePos;

	    while (1) {
	        // Read data into buffer
	        int bytesRead = fread(buffer + bufferOffset, 1, BUFFER_SIZE - bufferOffset, file);
	        if (bytesRead <= 0) {
	            if (bufferOffset > 0) {
	                // Send the last remaining NALU
	                send_nalu(buffer, bufferOffset);
	            }
	            break;
	        }

	        int totalBytes = bufferOffset + bytesRead;
	        startCodePos = find_start_code(buffer, 0, totalBytes);

	        while (startCodePos != -1) {
	            nextStartCodePos = find_start_code(buffer, startCodePos + 4, totalBytes);

	            if (nextStartCodePos != -1) {
	                int naluSize = nextStartCodePos - startCodePos;
	                send_nalu(buffer + startCodePos, naluSize);
	                startCodePos = nextStartCodePos;
	            } else {
	                // Move remaining data to the beginning of the buffer
	                memmove(buffer, buffer + startCodePos, totalBytes - startCodePos);
	                bufferOffset = totalBytes - startCodePos;
	                break;
	            }
	        }

	        if (nextStartCodePos == -1) {
	            bufferOffset = totalBytes;
	        }
	    }

	    fclose(file);
	}

	return (NULL);
}

void start_video_encoder(void (*pFunc)(void* data,int length)){
	pOnVideoFrameChanged = pFunc;
	int ret;
	if ((ret = pthread_create(&m_VideoEncoderTid, NULL, videoEncoderThread, NULL))
			!= 0) {
		LOGD("simulate_video encoder thread create failed!\n");
	} else {
		m_VideoEncoderThreadPtr = &m_VideoEncoderTid;
		LOGD("simulate_video encoder thread create successed!\n");
	}
}


void stop_video_encoder( ){
	keepEncoderRunning = 0;
	if( m_VideoEncoderThreadPtr != NULL )
	{
		pthread_join(m_VideoEncoderTid , NULL);
		m_VideoEncoderThreadPtr = NULL;
	}
}


